package com.sdsy.controller;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import com.sdsy.constant.Config;
import com.sdsy.constant.WalletConfig;
import com.sdsy.domain.TblTransferAccountDO;
import com.sdsy.domain.TransferAccountsExcelDO;
import com.sdsy.service.TblTransferAccountService;
import com.sdsy.service.TransferAccountsExcelService;
import com.sdsy.util.CommonUtil;
import com.sdsy.util.RecreationChainUtil;
import com.sdsy.util.SysUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Contract;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.ExecutionException;

/**
 * (TblTransferAccount)表控制层
 *
 * @author yxc
 * @since 2018-10-22 19:31:13
 */
@Controller
@RequestMapping("/tblTransferAccount")
public class TblTransferAccountController {


    private Logger logger = LoggerFactory.getLogger(TblTransferAccountController.class);
    @Autowired
    private Config config;
    @Autowired
    private WalletConfig walletConfig;
    /**
     * 服务对象
     */
    @Resource
    private TblTransferAccountService tblTransferAccountService;

    @Autowired
    private TransferAccountsExcelService transferAccountsExcelService;

    public static void main(String[] args) {


        String content = "yzl131431";

        // 随机生成密钥
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();

        // 构建
        AES aes = SecureUtil.aes(key);

        // 加密
        byte[] encrypt = aes.encrypt(content);
        // 解密
        byte[] decrypt = aes.decrypt(encrypt);

        // 加密为16进制表示
        String encryptHex = aes.encryptHex(content);
        // 解密为字符串
        String decryptStr = aes.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);

        System.out.println("encryptHex==============" + encryptHex);
        System.out.println("decryptStr==============" + decryptStr);
        System.out.println("remark==============" + Base64Utils.encodeToString(key));

    }


    @PostMapping("/batchTransfer")
    @ResponseBody
    public String batchTransfer() {
        long startTime = System.currentTimeMillis();
        String remark = "";
        int count = 0;

        List<TransferAccountsExcelDO> list = transferAccountsExcelService.queryList();
        logger.info("批量转账开始=============================" + list.size());
        if (!SysUtil.isNull(list) && list.size() > 0) {
            // 调用转账方法
            TransferAccountsExcelDO v1 = null;
            for (int i = 0, len = list.size(); i < len; i++) {
                try {
                    BigInteger GAS = Contract.GAS_LIMIT;
                    BigInteger GAS_PRICE = Contract.GAS_PRICE.add(new BigInteger(config.getGasPrice()));
                    RecreationChainUtil keySCode = getRecreationChainUtil(GAS, GAS_PRICE);
                    TransferAccountsExcelDO vo = list.get(i);
                    v1 = new TransferAccountsExcelDO();
                    v1.setId(vo.getId());
                    vo.setStatus("2");
                    boolean f = transferAccountsExcelService.updateTransferAccounts(vo);
                    if (f) {
                        /**  过滤钱包地址不正确的  **/
                        if (!SysUtil.isNull(vo.getToAccountNo())) {
                            logger.info("===========" + vo.getToAccountNo().trim().length());
                            if (42 == vo.getToAccountNo().trim().length()) {
                                TransactionReceipt receipt = keySCode.transfer(vo.getToAccountNo(), CommonUtil.aDouble(vo.getTransferNum())).sendAsync().get();
                                logger.info("receipt===========================" + receipt);
                                if (!SysUtil.isNull(receipt)) {
                                    String hash = receipt.getTransactionHash();
                                    logger.info("hash==================" + hash);
                                    vo.setHash(hash);
                                    vo.setStatus("1");
                                    vo.setFromAccountNo(config.getAddress());
                                    transferAccountsExcelService.updateTransferAccounts(vo);
                                    queryEth(receipt, GAS_PRICE);

                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error("batchTransfer异常=======================" + e.getMessage(), e);
                    count++;
                    v1.setStatus("3");
                    transferAccountsExcelService.updateTransferAccounts(v1);
                }
            }
            long endTime = System.currentTimeMillis();
            System.out.println("batchTransfer耗时 : " + (endTime - startTime) / 1000);
            int success = list.size() - count;
            remark = "成功是" + success + "条,失败是" + count + "条";
        } else {
            remark = "暂无数据";
        }

        return remark;
    }


    /**
     * @return
     */
    public String consumptionEth(String userGas, String gasPrice) {
        BigDecimal a = new BigDecimal(userGas).multiply(new BigDecimal(gasPrice));
        BigDecimal all = a.divide(new BigDecimal("1000000000000000000"), 6, BigDecimal.ROUND_HALF_UP);
        logger.info("all====================" + all);
        return all.toString();
    }


    /**
     *
     */
    @PostMapping("/transfer")
    @ResponseBody
    public boolean transfer(TransferAccountsExcelDO vo) {
        int count = 1;
        long startTime = System.currentTimeMillis();
        try {
            vo.setStatus("2");
            boolean f = transferAccountsExcelService.updateTransferAccounts(vo);
            if (f) {
                /**  过滤钱包地址不正确的  **/
                if (!SysUtil.isNull(vo.getToAccountNo())) {
                    logger.info("===========" + vo.getToAccountNo().trim().length());
                    if (42 == vo.getToAccountNo().trim().length()) {
                        BigInteger GAS = Contract.GAS_LIMIT;
                        BigInteger GAS_PRICE = Contract.GAS_PRICE.add(new BigInteger(config.getGasPrice()));
                        RecreationChainUtil keySCode = getRecreationChainUtil(GAS, GAS_PRICE);
                        TransactionReceipt receipt = keySCode.transfer(vo.getToAccountNo(), CommonUtil.aDouble(vo.getTransferNum())).sendAsync().get();
                        logger.info("单笔transfer转账==============" + receipt);
                        if (!SysUtil.isNull(receipt)) {
                            String hash = receipt.getTransactionHash();
                            logger.info("hash=================" + hash);
                            vo.setHash(hash);
                            vo.setStatus("1");
                            vo.setFromAccountNo(config.getAddress());
                            transferAccountsExcelService.updateTransferAccounts(vo);
                            queryEth(receipt, GAS_PRICE);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("transfer异常=======================" + e.getMessage(), e);
            count = 0;
            vo.setStatus("3");
            transferAccountsExcelService.updateTransferAccounts(vo);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("transfer耗时 : " + (endTime - startTime) / 1000);
        if (count > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param receipt
     * @param GAS_PRICE
     */
    public void queryEth(TransactionReceipt receipt, BigInteger GAS_PRICE) {
        if (!SysUtil.isNull(receipt.getGasUsed())) {
            String eth = null;
            try {
                eth = consumptionEth(receipt.getGasUsed().toString(), GAS_PRICE.toString());
            } catch (Exception e) {
                e.printStackTrace();
                logger.info("查看消耗的eth==================" + e.getMessage(), e);
            }
            logger.info("消耗的eth===============" + eth);
        }
    }


    /**
     * @return
     */
    public RecreationChainUtil getRecreationChainUtil(BigInteger GAS, BigInteger GAS_PRICE) {
        long startTime = System.currentTimeMillis();
        logger.info("实例化合约对象开始=====================");
        // 创建Web3J
        Web3j web3 = Web3j.build(new HttpService("https://mainnet.infura.io/c8LMJFLLL7rUEBtxozvJ"));
//
        // 加载钱包
        Credentials credentials = null;
        try {
            // 构建
            byte[] key = Base64Utils.decodeFromString(config.getRandom());
            AES aes = SecureUtil.aes(key);
            String decryptStr = aes.decryptStr(config.getPassword(), CharsetUtil.CHARSET_UTF_8);
            credentials = WalletUtils.loadCredentials(decryptStr, new File(config.getAccount_path()));
        } catch (CipherException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //加载合约
        logger.info("GAS==================" + GAS + "&GAS_PRICE==================" + GAS_PRICE);
        RecreationChainUtil keySCode = RecreationChainUtil.load(config.getSmart_contract_address(), web3, credentials, GAS_PRICE, GAS);
        logger.info("实例化合约对象结束=====================");
        long endTime = System.currentTimeMillis();
        System.out.println("实例化合约对象耗时 : " + (endTime - startTime) / 1000);
        return keySCode;
    }

}
